package com.skyworthclub.competition.config.security;

import com.skyworthclub.competition.config.async.SpringAsyncConfig;
import com.skyworthclub.competition.config.security.handler.CustomAuthenticationFailHandler;
import com.skyworthclub.competition.config.security.handler.CustomAuthenticationSuccessHandler;
import com.skyworthclub.competition.config.security.handler.CustomLogoutSuccessHandler;
import com.skyworthclub.competition.config.security.handler.RestAccessDeniedHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author li
 * @version 1.0
 * @since 18-10-15 下午6:50
 * 权限管理配置类
 **/
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String LOGIN_URL = "/api/auth/login";

    private Logger logger = LoggerFactory.getLogger(SpringAsyncConfig.class);

    /**
     * 注入认证成功后的处理器
     */
    private final CustomAuthenticationSuccessHandler authenticationSuccessHandler;
    /**
     * 注入认证失败后的处理器
     */
    private final CustomAuthenticationFailHandler authenticationFailHandler;
    /**
     * 注入用户信息获取服务实例（自定义从数据库获取用户信息）
     */
    private final CustomUserDetailsServiceImpl userDetailsService;
    /**
     * 注入成功注销的处理器
     */
    private final CustomLogoutSuccessHandler customLogoutSuccessHandler;
    /**
     * 注入无权限请求处理器
     */
    private final RestAccessDeniedHandler restAccessDeniedHandler;
    private final RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    public SpringSecurityConfig(CustomAuthenticationSuccessHandler authenticationSuccessHandler,
                                CustomAuthenticationFailHandler authenticationFailHandler,
                                CustomUserDetailsServiceImpl userDetailsService,
                                CustomLogoutSuccessHandler customLogoutSuccessHandler,
                                RestAccessDeniedHandler restAccessDeniedHandler,
                                RestAuthenticationEntryPoint restAuthenticationEntryPoint) {
        this.authenticationSuccessHandler = authenticationSuccessHandler;
        this.authenticationFailHandler = authenticationFailHandler;
        this.userDetailsService = userDetailsService;
        this.customLogoutSuccessHandler = customLogoutSuccessHandler;
        this.restAccessDeniedHandler = restAccessDeniedHandler;
        this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
    }

    /**
     * 重用父类的AuthenticationManager
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() {
        try {
            return super.authenticationManagerBean();
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return null;
    }


    /** 注入自定义UsernamePasswordAuthenticationFilter过滤器
     * @return Bean
     */
    @Bean
    public CustomUsernamePasswordAuthenticationFilter customUsernamePasswordAuthenticationFilter() {
        CustomUsernamePasswordAuthenticationFilter filter = new CustomUsernamePasswordAuthenticationFilter();
        //重用WebSecurityConfigurerAdapter配置的AuthenticationManager，重点！！！！
        filter.setAuthenticationManager(authenticationManagerBean());
        //设置拦截路径为登录路径
        filter.setFilterProcessesUrl(LOGIN_URL);
        //设置认证完成后的处理器
        filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
        filter.setAuthenticationFailureHandler(authenticationFailHandler);

        return filter;
    }

    /**
     * 不拦截静态资源路径，静态资源交给nginx管理后可有可无
     */
    @Override
    public void configure(WebSecurity web){
        web.ignoring().antMatchers("/static/**", "/webjars/**");
    }

    /**
     * 权限访问控制
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //后台管理
        http.authorizeRequests()
                .antMatchers("/api/admin/**", "/druid/stat")
                .hasAuthority("admin");

        //访问首页不需要权限
        http.authorizeRequests()
                .antMatchers("/", "/index.html")
                .permitAll();
        //访问比赛不需要权限
        http.authorizeRequests()
                .antMatchers("/api/match/list/*", "/api/match/detail/*", "/api/match/carouselFigure")
                .permitAll();
        //登录，注册，重置密码，获取验证码不需要权限
        http.authorizeRequests()
                .antMatchers(LOGIN_URL, "/auth/loginPage", "/api/auth/register", "/api/auth/resetPassword", "/api/auth/sendValidationCode")
                .permitAll();

        //查看评论不需要认证
        http.authorizeRequests()
                .antMatchers("/api/comment/list/**")
                .permitAll();

        //搜索不需要权限
        http.authorizeRequests()
                .antMatchers("/api/search/**","/api/search")
                .permitAll();

        //登录认证功能
        http.authorizeRequests()
                .and()
                .formLogin()    //表单登录配置
                //重定向登录页面
                .loginPage("/api/auth/loginPage")
                //登录认证处理路径
                .loginProcessingUrl(LOGIN_URL)
                .successHandler(authenticationSuccessHandler)
                .failureHandler(authenticationFailHandler)
                .permitAll();

        //覆盖UsernamePasswordAuthenticationFilter过滤器对ajax登录和表单登录分别处理
        http.addFilterAt(customUsernamePasswordAuthenticationFilter(),
                UsernamePasswordAuthenticationFilter.class);

        //注销登录处理
        http.authorizeRequests()
                .and()
                .logout()
                .logoutUrl("/api/auth/logout")
                .logoutSuccessHandler(customLogoutSuccessHandler)
                .permitAll();


        //允许跨域请求
        http.authorizeRequests()
                .and()
                .csrf()
                .disable();

        //其他路径均需要通过认证
        http.authorizeRequests()
                .anyRequest()
                .authenticated();

        //指定无权限请求处理器（适用于已登录认证的用户）
        http.exceptionHandling().accessDeniedHandler(restAccessDeniedHandler);
        //未登录用户访问没有相应权限的页面的处理
        http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint);

    }

    /**
     * 自定义用户信息获取来源和加密算法
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
     * 选择加密算法（BCryptPasswordEncoder BCrypt）并注入IOC容器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}
